23.7 Ausdruck von mehrseitigem Text
 
Zum Abschluss der Thematik des Druckens folgt nun noch ein Beispielprogramm, dass den Inhalt einer Textbox formatiert ausgibt und außerdem berücksichtigt, dass der Ausdruck unter Umständen mehrere Druckseiten lang ist.
Dazu stelle ich Ihnen das Programm, das auf den hochtrabenden Namen Texteditor lautet, vor. Ein wenig überzogen ist der Name aus dem Grund, weil im Wesentlichen nur die mit dem Drucken eines Dokuments zusammenhängenden Möglichkeiten üblicher Texteditoren implementiert sind. Es ist allerdings nicht sonderlich schwierig, das Programm zu ergänzen – beispielsweise um das Menü Bearbeiten mit den Auswahlmöglichkeiten Kopieren, Ausschneiden und Einfügen.
Sehen wir uns nun den Programmcode zusammengefasst an – allerdings zunächst nur die Passagen, die keiner genaueren Erklärung bedürfen. Dazu gehört jedoch nicht der Ereignishandler des PrintPage-Ereignisses, den wir anschließend Zeile für Zeile behandeln werden.
| // --------------------------------------------------------------
|
| // Beispiel: ...\Kapitel 23\Texteditor
|
| // --------------------------------------------------------------
|
| ...
|
| public partial class Form1 : Form {
|
| ...
|
| // Startseite zum Drucken
|
| private int startSeite;
|
| // Anzahl der Druckseiten
|
| private int anzahlSeiten;
|
| // aktuelle Seitenzahl
|
| private int seitenNummer;
|
| // zu druckender Text
|
| private string strPrintText;
|
| // der Name der geöffneten Datei
|
| private string strDateiName;
|
| private void mnuDrucken_Click(object sender, EventArgs e) {
|
| printDialog1.AllowSomePages = true;
|
| if (printDialog1.ShowDialog() == DialogResult.OK) {
|
| printDocument1.DocumentName = "Dokument";
|
| // Startwerte abhängig vom zu druckenden
|
| // Text initialisieren
|
| switch (printDialog1.PrinterSettings.PrintRange) {
|
| case PrintRange.AllPages:
|
| strPrintText = textBox1.Text;
|
| startSeite = 1;
|
| anzahlSeiten = printDialog1.PrinterSettings.MaximumPage;
|
| break;
|
| case PrintRange.SomePages:
|
| strPrintText = textBox1.Text;
|
| startSeite = printDialog1.PrinterSettings.FromPage;
|
| anzahlSeiten = printDialog1.PrinterSettings.ToPage
|
| – startSeite + 1;
|
| break;
|
| }
|
| // Drucken starten
|
| seitenNummer = 1;
|
| printDocument1.Print();
|
| }
|
| }
|
| // das Menü 'Öffnen'
|
| private void mnuOpen_Click(object sender, EventArgs e) {
|
| openFileDialog1.Filter = "Text Documents(*.txt) " +
|
| " |*.txt|All Files(*.*)|*.*";
|
| openFileDialog1.FileName = "*.txt";
|
| StreamReader sr;
|
| if (openFileDialog1.ShowDialog() == DialogResult.OK)
|
| try {
|
| this.strDateiName = openFileDialog1.FileName;
|
| sr = new StreamReader(this.strDateiName);
|
| textBox1.Text = sr.ReadToEnd();
|
| sr.Close();
|
| }
|
| catch (Exception exc) {
|
| MessageBox.Show(exc.Message, "Texteditor",
|
| MessageBoxButtons.OK,
|
| MessageBoxIcon.Asterisk);
|
| return;
|
| }
|
| this.Text += this.strDateiName;
|
| textBox1.SelectionStart = 0;
|
| textBox1.SelectionLength = 0;
|
| }
|
| private void mnuPrintPreview_Click(object sender, EventArgs e) {
|
| printPreviewDialog1.Document = printDocument1;
|
| printDialog1.AllowSomePages = true;
|
| strPrintText = textBox1.Text;
|
| startSeite = 1;
|
| anzahlSeiten = printDialog1.PrinterSettings.MaximumPage;
|
| seitenNummer = 1;
|
| printPreviewDialog1.ShowDialog();
|
| }
|
| private void mnuPageSetup_Click(object sender, EventArgs e) {
|
| pageSetupDialog1.ShowDialog();
|
| }
|
| }
|
Der Ereignishandler von »PrintPage«
Im PrintPage-Ereignishandler befindet sich die gesamte Steuerung des Druckvorgangs. Dazu gehören die Ermittlung der für den Textausdruck zur Verfügung stehenden Rechteckfläche und die Anzahl der Druckzeilen jeder Seite. Dabei darf die letzte Zeile der Seite nicht abgeschnitten werden. Zusätzlich soll jede gedruckte Dokumentenseite eine Kopfzeile mit dem Dateinamen enthalten und in einer Fußzeile die aktuelle Seitezahl anzeigen.
Nach der Deklaration einiger lokaler Variablen, die innerhalb des Ereignishandler benötigt werden, wird mit
| rectFPapier = e.MarginBounds;
|
der Druckbereich des gesamten Ausdrucks festgelegt.
rectFText legt die Größe des Rechtecks fest, in dem der Text aus der Textbox ausgedruckt wird. Die Methode Inflate der Klasse Rectangle vergrößert/verkleinert eine Rechteckfläche. In unserem Fall subtrahieren wir Kopf- und Fußzeile.
| rectFText = RectangleF.Inflate(rectFPapier,
|
| 0, –2 * textBox1.Font.GetHeight(e.Graphics));
|
Die Anzahl der Zeilen muss in jedem Fall durch eine gerade Zahl beschrieben werden. Die statische Methode Floor der Klasse Math gibt die größte ganze Zahl zurück, die kleiner oder gleich der angegebenen Zahl ist. Das ist das, was wir benötigen.
| int anzahlZeilen = (int)Math.Floor(rectFText.Height /
|
| textBox1.Font.GetHeight(e.Graphics));
|
Daran schließt sich die Definition des Stringformats an, um den Umbruch des Textes zu beeinflussen.
| stringFormat.Trimming = StringTrimming.Word;
|
Nun sind alle vorbereitenden Initialisierungen beendet, und es kann prinzipiell zur ersten Druckanweisung kommen. Zuvor müssen wir aber noch feststellen, welche Seite als erste ausgedruckt werden soll. Schließlich hat der Anwender die Option, den Ausdruck ab einer beliebigen Seitenzahl zu starten.
| while ((seitenNummer < startSeite) && (strPrintText.Length > 0))
|
| {
|
| e.Graphics.MeasureString(strPrintText, textBox1.Font,
|
| rectFText.Size, stringFormat,
|
| out intChars, out intLines);
|
| strPrintText = strPrintText.Substring(intChars);
|
| seitenNummer++;
|
| }
|
Sollte kein Text mehr zum Ausdruck zur Verfügung stehen, muss der Druck beendet werden. In diesem Fall ist die Länge der Variablen strPrintText, die immer den aktuell noch auszudruckenden Text enthält und nach jeder gedruckten Seite aktualisiert wird, gleich null.
| if (strPrintText.Length == 0) {
|
| e.Cancel = true;
|
| return;
|
| }
|
Mit
| e.Graphics.DrawString(strPrintText, textBox1.Font,
|
| Brushes.Black, rectFText, stringFormat);
|
| strPrintText = strPrintText.Substring(intChars);
|
wird der Text ausgegeben und der Inhalt der Variablen strPrintText um den ausgedruckten Teil reduziert. Damit sind wir nahezu am Ziel, müssen aber zuerst noch die Kopf- und Fußzeile an den Drucker schicken:
| stringFormat.Alignment = StringAlignment.Center;
|
| e.Graphics.DrawString(this.strDateiName, textBox1.Font, Brushes.Black,
|
| rectFPapier, stringFormat);
|
| stringFormat.LineAlignment = StringAlignment.Far;
|
| e.Graphics.DrawString("Page " + seitenNummer, textBox1.Font, Brushes.Black,
|
| rectFPapier, stringFormat);
|
Ob eine weitere Seite ausgedruckt werden muss, wird durch die Eigenschaft HasMorePages zum Ausdruck gebracht. Wenn keine weiteren Druckseiten folgen, müssen alle im Ereignishandler benutzten Variablen wieder auf ihren Initialisierungswert zurückgesetzt werden.
| e.HasMorePages = (strPrintText.Length > 0) &&
|
| (seitenNummer < startSeite + anzahlSeiten);
|
| if (!e.HasMorePages)
|
| {
|
| strPrintText = textBox1.Text;
|
| startSeite = 1;
|
| anzahlSeiten = printDialog1.PrinterSettings.MaximumPage;
|
| seitenNummer = 1;
|
| }
|
Damit ist das PrintPage-Ereignis fertig implementiert. Der Programmcode sieht vollständig wie folgt aus:
| private void printDocument1_PrintPage(object sender, PrintPageEventArgs e)
|
| {
|
| StringFormat stringFormat = new StringFormat();
|
| RectangleF rectFPapier, rectFText;
|
| int intChars, intLines;
|
| // Ermitteln des Rectangles, das den gesamten Druckbereich
|
| // beschreibt (inklusive Kopf- und Fusszeile)
|
| rectFPapier = e.MarginBounds;
|
| // Ermitteln des Rectangles, das den Bereich für den
|
| // Text beschreibt (ausschließlich Kopf- und Fusszeile)
|
| rectFText = RectangleF.Inflate(rectFPapier, 0,
|
| –2 * textBox1.Font.GetHeight(e.Graphics));
|
| // eine gerade Anzahl von Druckzeilen ermitteln
|
| int anzahlZeilen = (int)Math.Floor(rectFText.Height /
|
| textBox1.Font.GetHeight(e.Graphics));
|
| // die Höhe des textbeinhaltenden Rechtecks festlegen, damit die
|
| // letzte Druckzeile nicht abgeschnitten wird
|
| rectFText.Height = anzahlZeilen *
|
| textBox1.Font.GetHeight(e.Graphics);
|
| // das StringFormat-Objekt festlegen, um den Text in einem
|
| // Rechteck anzuzeigen – Text bis zum nächstliegenden Wort
|
| // verkürzen
|
| stringFormat.Trimming = StringTrimming.Word;
|
| // legt die Druckstartseite fest, wenn es sich nicht um die
|
| // erste Dokumentenseite handelt
|
| while ((seitenNummer < startSeite) && (strPrintText.Length > 0)) {
|
| e.Graphics.MeasureString(strPrintText, textBox1.Font,
|
| rectFText.Size, stringFormat,
|
| out intChars, out intLines);
|
| strPrintText = strPrintText.Substring(intChars);
|
| seitenNummer++;
|
| }
|
| // Druckjob beenden, wenn es keinen Text zum Drucken mehr gibt
|
| if (strPrintText.Length == 0) {
|
| e.Cancel = true;
|
| return;
|
| }
|
| // den Text an das Graphics-Objekt übergeben
|
| e.Graphics.DrawString(strPrintText, textBox1.Font, Brushes.Black,
rectFText, stringFormat);
|
| // Text für die nächste Seite
|
| // intChars – Anzahl der Zeichen in der Zeichenfolge
|
| // intLines – Anzahl der Zeilen in der Zeichenfolge
|
| e.Graphics.MeasureString(strPrintText, textBox1.Font,
|
| rectFText.Size, stringFormat,
|
| out intChars, out intLines);
|
| strPrintText = strPrintText.Substring(intChars);
|
| // StringFormat restaurieren
|
| stringFormat = new StringFormat();
|
| // Dateiname in der Kopfzeile anzeigen
|
| stringFormat.Alignment = StringAlignment.Center;
|
| e.Graphics.DrawString(this.strDateiName, textBox1.Font,
|
| Brushes.Black, rectFPapier, stringFormat);
|
| // Seitennummer in der Fusszeile anzeigen
|
| stringFormat.LineAlignment = StringAlignment.Far;
|
| e.Graphics.DrawString("Page " + seitenNummer, textBox1.Font,
|
| Brushes.Black,rectFPapier, stringFormat);
|
| // ermitteln, ob weitere Seiten zu drucken sind
|
| seitenNummer++;
|
| e.HasMorePages = (strPrintText.Length > 0) &&
|
| (seitenNummer < startSeite + anzahlSeiten);
|
| // Neuinitialisierung
|
| if (!e.HasMorePages) {
|
| strPrintText = textBox1.Text;
|
| startSeite = 1;
|
| anzahlSeiten = printDialog1.PrinterSettings.MaximumPage;
|
| seitenNummer = 1;
|
| }
|
| }
|
|